/**
 * Copyright (c) 2010 Andrey Somov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.yaml.scalayaml.reader

/**
 version: 1.1 / 2007-01-25
 - changed BOM recognition ordering (longer boms first)

 Original pseudocode   : Thomas Weidenfeller
 Implementation tweaked: Aki Nieminen
 Implementation changed: Andrey Somov 
 * UTF-32 removed because it is not supported by YAML
 * no default encoding

 http://www.unicode.org/unicode/faq/utf_bom.html
 BOMs:
 EF BB BF       = UTF-8,
 FE FF          = UTF-16, big-endian
 FF FE          = UTF-16, little-endian

 Win2k Notepad:
 Unicode format = UTF-16LE
 **/

import java.io._

/**
 * Generic unicode textreader, which will use BOM mark to identify the encoding
 * to be used. If BOM is not found then use a given default or system encoding.
 * @param in - InputStream to be read
 */
class UnicodeReader (in: java.io.InputStream) extends java.io.Reader {
    val BOM_SIZE = 3
    val internalIn = new PushbackInputStream(in, BOM_SIZE)
    var internalIn2: InputStreamReader = null
    
    /**
     * Read-ahead four bytes and check for BOM marks. Extra bytes are unread
     * back to the stream, only BOM bytes are skipped.
     */
    def init {
        if (internalIn2 != null)
            return;

        var encoding: String = null
        var bom = new Array[Byte](BOM_SIZE)
        var unread: Int = 0
        var n = internalIn.read(bom)
        def compare(i:Int, b: Int) = {
        	bom(i) == b.toByte
        }
        if (compare(0, 0xEF) && compare(1, 0xBB) && compare(2, 0xBF)) {
            encoding = "UTF-8"
            unread = n - 3
        } else if (compare(0, 0xFE) && compare(1, 0xFF)) {
            encoding = "UTF-16BE"
            unread = n - 2
        } else if (compare(0, 0xFF) && compare(1, 0xFE)) {
            encoding = "UTF-16LE"
            unread = n - 2
        } else {
            // Unicode BOM mark not found, unread all bytes
            encoding = "UTF-8"
            unread = n
        }

        if (unread > 0)
            internalIn.unread(bom, (n - unread), unread)

        // Use given encoding
        internalIn2 = new InputStreamReader(internalIn, encoding)
    }
  
    def close {
        init
        internalIn2.close
    }
    
    def read(cbuf: Array[Char], off: Int, len: Int) = {
        init
        internalIn2.read(cbuf, off, len)
    }
}  
